#!/usr/bin/env python

from __future__ import print_function

import butlerplugins
import re
import string
import threading
import time
import os
import subprocess
import webbrowser

import platform_specific
import butler_registry

class ExecCommand(threading.Thread):
    def __init__(self, command, cmdstr, cmdargs):
        self.cmd = command
        self.cmdstr = cmdstr
        self.cmdargs = cmdargs

        threading.Thread.__init__(self)
        if hasattr(command, 'isdaemon'):
            self.daemon = command.isdaemon

    def run(self):
        self.cmd(self.cmdstr, self.cmdargs)

def call(*args):
    '''A wrapper for Popen that dsables std[in/out/err].

    '''
    PIPE = subprocess.PIPE
    p = subprocess.Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    
def open_url(url):
    '''Opens the specified web address.
    '''
    webbrowser.open(url)

def remove_trigger(trigger, command):
    '''Removes a trigger from a command, returns the command without
    the trigger, and as a split list.
    i.e.:
    Command: random integer 43 54
    Trigger: randomint

    return: ("43 54", ["43", "54"])

    '''
    #Get trigger's last char.
    lc = trigger[-1]

    #Find number of those chars in trigger.
    c = string.count(trigger, lc)

    #Find occurance c of that char in the command.
    ind = string.index(command, lc, c)

    #Cut off the trigger from the rest of the command.
    command = command[ind + 1:]

    #Cut off everything before the next space as it may be a trailing
    #part of the trigger.
    try:
        command = command[string.index(command, ' ')+1:]
    except ValueError:
        pass
    return command, string.split(command)

def butler_command(b):
    while 1:
        try:
            #Await input.
            if b.command_hist:
                j = raw_input("Butler ~> ")
            else:
                j = "about"

            b.command(j)

        except KeyboardInterrupt:
            exit_procedure()
        except ValueError:  #Thread called shutdown.
            exit_procedure()

def exit_procedure():
    #Save registry
    f = platform_specific.REGISTRY_PATH

    butlerplugins.registry.dump_to_file(f)

    #Exit Butler.
    print ("")
    exit()


class Butler:
    command_hist = []
    command_dict = {}
    possible_commands = []

    core_command_dict = {}
    possible_core_commands = []

    networking_send = None  #The networking send instance.

    def strip_text(self, text):
        '''Converts the given text to lower case and only a-z.'''
        return re.sub('[^a-z]', '', text.lower().strip())


    def check_and_run(self, searchtext, command, pc, cd):
        '''Looks for the text in the possible commands list pc, and
        then runs the command if found from the command dictionary cd.

        Returns True if command was run, False if one was not.

        Paramters:
        searchtext - The text to search for the command trigger in.
        command - The full text of the command.
        pc - The list of possible command triggers.
        cd - A dictionary mapping the command trigger to the command.

        '''
        searchtext = self.strip_text(command)

        for i in pc:
            if searchtext.startswith(i):
                cmdstr, cmdargs = remove_trigger(i, command)

                k = ExecCommand(cd[i], cmdstr, cmdargs)
                k.start()

                #If the thread hasn't said that it wants to be daemonized
                #wait for it.
                if not hasattr(cd[i], 'isdaemon') or not cd[i].isdaemon:
                    k.join()

                return True


    def command(self, cmd):
        '''Runs butler against the current set of items with the
        command string given.

        Core commands get evaluated first.

        '''

        self.command_hist.append(cmd)
        j = str(cmd)  #Make sure what is input is a string.
        jsimple = self.strip_text(j)

        if jsimple in ['exit','quit']:
            exit_procedure()

        elif jsimple == "":
            return True  #Skip blank commands.

        else:
            if "|" in j:
                a, c = j.split("|",2)
                self.networking_send(a.strip(), c.strip())
                return True
            found = self.check_and_run(jsimple, j, self.possible_core_commands, self.core_command_dict)

            if not found:
                found = self.check_and_run(jsimple, j, self.possible_commands, self.command_dict)

            if not found:
                #If the command wasn't found.
                print("Butler doesn't know how to do that, try writing a plugin, it is really quite elementary")
                print("  - Joe")

            return found


if __name__ == "__main__":
    b = Butler()

    #Load registry.
    f = platform_specific.REGISTRY_PATH
    butlerplugins.registry = butler_registry.Registry(f)

    registry = butler_registry.Registry()

    #Load plugins.
    core_plugins_list = platform_specific.CORE_PLUGIN_LIST
    core_plugins_builtins = {'butler':b, 'registry':registry, 'call':call}

    user_plugins_list = platform_specific.STANDARD_PLUGIN_LIST
    user_plugins_builtins = {'registry':registry, 'call':call, 'open_url':open_url}

    b.core_command_dict = butlerplugins.init_plugins(core_plugins_list, core_plugins_builtins)  #Dictionary of commands and functions to execute from them.
    b.possible_core_commands = butlerplugins.gen_possible_commands(b.core_command_dict)

    b.command_dict = butlerplugins.init_plugins(user_plugins_list, user_plugins_builtins)
    b.possible_commands = butlerplugins.gen_possible_commands(b.command_dict)


    #Begin the butler command prompt
    butler_command(b)